home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 17 / CU Amiga Magazine's Super CD-ROM 17 (1997)(EMAP Images)(GB)[!][issue 1997-12].iso / CUCD / Programming / DiceSource / src / derror / DError.c < prev    next >
C/C++ Source or Header  |  1997-09-09  |  34KB  |  1,202 lines

  1. /*
  2.  *    (c)Copyright 1992-1997 Obvious Implementations Corp.  Redistribution and
  3.  *    use is allowed under the terms of the DICE-LICENSE FILE,
  4.  *    DICE-LICENSE.TXT.
  5.  */
  6. //***********************************************************************
  7. //*  Copyright (c) 1993, 1994 Obvious Implementation Corp.              *
  8. //*                           All Rights Reserved.                      *
  9. //*                     207 Livingstone Drive,                          *
  10. //*                     Cary N.C. 27513 - USA                           *
  11. //***********************************************************************
  12.  
  13. #include <stdio.h>
  14. #include <exec/types.h>
  15. #include <proto/exec.h>
  16. #include <proto/dos.h>
  17. #include <dos/dos.h>
  18. #include <dos/dosextens.h>
  19. #include <string.h>
  20. #include <stdlib.h>
  21. #include <clib/exec_protos.h>
  22. #include <lib/rexx.h>
  23. #include <lib/misc.h>
  24.  
  25. #include "DError_rev.h"
  26.  
  27. #define PORTNAME "DICE_ERROR_PARSER"  // This is what everyone else calls us by
  28.  
  29. extern __stkargs LONG SetRexxVar(struct RexxMsg *,char *,char *,ULONG);
  30.  
  31. struct ErrorInfo {
  32.     struct ErrorInfo *next;      // Next error line in the list
  33.     struct ErrorInfo *prev;      // Previous line in the list
  34.     char             line[1];    // Text of the line.  Note the [1] for the NULL
  35. };
  36.  
  37. struct FileInfo {
  38.     struct FileInfo *next;       // Next file entry in the list
  39.     struct FileInfo *prev;       // Previous file entry in the list
  40.     char            *source;     // Source file that was compiled
  41.     char            *dir;        // Directory the file was compiled in
  42.     char            *args;       // Rexx arguments used for the compile
  43.     struct ErrorInfo base;       // Root node for all lines
  44.     struct ErrorInfo *cur;       // Current active error line
  45. };
  46.  
  47. struct FileInfo files;           // Root node for all files
  48. struct FileInfo *curfile;        // Current active error file
  49.  
  50. char *RexxHostName = NULL;       // We will create the host ourseleves
  51.  
  52. #define MAX_FILENAME 1024
  53. char buf[MAX_FILENAME+1];
  54. char ebuf[512];             // AREXX return string holding buffer
  55.  
  56. short run_arexx_server;
  57.  
  58. //***************************************************************************
  59. //* Procedure: AddLine                                                      *
  60. //* Synopsis:  AddLine(FileInfo *, char *)                                  *
  61. //* Purpose:   Adds the line to the current file information.  If there is  *
  62. //*            not enough memory, it will simply ignore the line silently.  *
  63. //***************************************************************************
  64. void AddLine(struct FileInfo *fi, char *line)
  65. {
  66.    struct ErrorInfo *ei;
  67.  
  68.    ei = malloc(sizeof(struct ErrorInfo) + strlen(line));
  69.    if (ei)
  70.    {
  71.       ei->prev = fi->base.prev;
  72.       ei->next = fi->base.prev->next;
  73.       strcpy(ei->line, line);
  74.       fi->base.prev->next = ei;
  75.       fi->base.prev = ei;
  76.    }
  77. }
  78.  
  79. //******************************************************************************
  80. //* Procedure: NewFile
  81. //* Synopsis:  FileInfo = NewFile(char *source, char *dir, char *args)
  82. //* Purpose:   Creates a new file info.  If it is unable to, it returns NULL
  83. //******************************************************************************
  84. struct FileInfo *NewFile(char *source, char *dir, char *args)
  85. {
  86.    struct FileInfo *fi;
  87.  
  88.    fi = malloc(sizeof(struct FileInfo));
  89.    if (fi)
  90.    {
  91.       fi->source = strdup(source);
  92.       fi->dir = strdup(dir);
  93.       fi->args = strdup(args);
  94.       fi->cur  = NULL;
  95.       if (fi->source != NULL &&
  96.           fi->dir    != NULL &&
  97.           fi->args   != NULL)
  98.       {
  99.           fi->prev = files.prev;
  100.           fi->next = files.prev->next;
  101.           files.prev->next = fi;
  102.           files.prev = fi;
  103.  
  104.           fi->base.prev = fi->base.next = &fi->base;
  105.       }
  106.       else
  107.       {
  108.          if (fi->source) free(fi->source);
  109.          if (fi->dir)    free(fi->dir);
  110.          if (fi->args)   free(fi->args);
  111.          free(fi);
  112.          fi = NULL;
  113.       }
  114.    }
  115.    return(fi);
  116. }
  117.  
  118. //************************************************************************
  119. //* Procedure: FreeFile
  120. //* Synopsis:  FreeFile(struct FileInfo *)
  121. //* Purpose:   Removes a FileInfo structure from the list
  122. //************************************************************************
  123. void FreeFile(struct FileInfo *fi)
  124. {
  125.    struct ErrorInfo *ei;
  126.  
  127.    //
  128.    // Handle any attempts to free the base FI
  129.    //
  130.    if (fi == &files) return;
  131.  
  132.    // Unlink us from the chain of file handles
  133.    fi->prev->next = fi->next;
  134.    fi->next->prev = fi->prev;
  135.  
  136.    //
  137.    // Update any system pointers which might look at what we are going to free
  138.    //
  139.    if (curfile == fi)
  140.    {
  141.       curfile = NULL;  // no more anyway
  142.    }
  143.  
  144.    //
  145.    // Free all the lines
  146.    //
  147.    fi->base.prev->next = NULL;  // mark our stopping point
  148.    for(ei = fi->base.next; ei != NULL;)
  149.    {
  150.       struct ErrorInfo *sei;
  151.  
  152.       sei = ei;
  153.       ei = ei->next;
  154.       free(sei);
  155.    }
  156.  
  157.    //
  158.    // Finally, get rid of the file information
  159.    //
  160.    free(fi->source);
  161.    free(fi->dir);
  162.    free(fi->args);
  163.    free(fi);
  164. }
  165.  
  166. //***********************************************************************
  167. //* Procedure: say
  168. //* Synopsis:  (void)say(msg);
  169. //* Purpose:   Displays the given message on the console
  170. //***********************************************************************
  171. void say(char *msg)
  172. {
  173.    BPTR out;
  174.    out = Output();
  175.    if (out)
  176.    {
  177.       Write(out, msg, strlen(msg));
  178.       Write(out, "\n", 1);
  179.    }
  180. }
  181.  
  182. //***********************************************************************
  183. //* Procedure: usage
  184. //* Synopsis:  (void)usage();
  185. //* Purpose:   Displays the command line usage message - does not return
  186. //***********************************************************************
  187. void usage(void)
  188. {
  189.    say("FILE/M,MACRO/K,PROJECT/K,REXXSTARTUP/S" VERSTAG);
  190.    exit(20);
  191. }
  192.  
  193. //***********************************************************************
  194. //* Procedure: dottx
  195. //* Synopsis:  result = dottx(port, cmd);
  196. //* Purpose:   Sends a command to TurboText
  197. //***********************************************************************
  198. char *dottx(char *port, char *cmd)
  199. {
  200.    char *res;
  201.    long ec;
  202.  
  203.    if (port == NULL) port = "TURBOTEXT";
  204.    PlaceRexxCommandDirect(NULL, port, cmd, &res, &ec);
  205.  
  206.    return(res);
  207. }
  208.  
  209. //***********************************************************************
  210. //* Procedure: full_path
  211. //* Synopsis:  path = full_path(name)
  212. //* Purpose:   Constructs a fully expanded filename
  213. //*            Note, we can not assume that the file exists, so it will
  214. //*            not be possible to actually lock it.  We can assume that
  215. //*            the directory it is part of does exist.  Note that it can
  216. //*            return NULL if there is no memory available.
  217. //***********************************************************************
  218. char *full_path(char *name)
  219. {
  220.    BPTR lock;
  221.    __aligned struct FileInfoBlock fib;
  222.    char *tail, *p;
  223.    int pos;
  224.  
  225.    //
  226.    // Step 1 - split out any directory information from the actual name
  227.    //
  228.    p = strrchr(name, '/');
  229.    if (p == NULL) p = strrchr(name, ':');
  230.    if (p != NULL)
  231.    {
  232.       //
  233.       // There was some directory information involved
  234.       //
  235.       char c;
  236.       tail = strdup(p+1);
  237.       c = p[1];
  238.       p[1] = 0;
  239.       lock = Lock(name, SHARED_LOCK);
  240.       p[1] = c;
  241.    }
  242.    else
  243.    {
  244.       //
  245.       // No directory information involved, just the name relative to the
  246.       // current directory
  247.       //
  248.       lock = Lock("", SHARED_LOCK);
  249.       tail = strdup(name);
  250.    }
  251.  
  252.    //
  253.    // Step 2 - we have the lock on the directory and the tail part of the name
  254.    // We want to construct a fully qualified path for the directory.
  255.    // If for some reason the lock on the directory returned 0, we want to just
  256.    // return the name they gave us to begin with.
  257.    //
  258.    if (lock == 0)
  259.    {
  260.       free(tail);
  261.       return(strdup(name));
  262.    }
  263.  
  264.    //
  265.    // Step 3 - Fully qualify the directory portion into the buffer
  266.    //
  267.    if (DOSBase->dl_lib.lib_Version >= 36)
  268.    {
  269.       if (!NameFromLock(lock, buf, MAX_FILENAME))
  270.       {
  271.          //
  272.          // Either the name is too long or there was something else wrong with
  273.          // the file name, just return what they gave us as a start
  274.          //
  275.          UnLock(lock);
  276.          free(tail);
  277.          return(strdup(name));
  278.       }
  279.       UnLock(lock);
  280.       pos = 0;
  281.    }
  282.    else
  283.    {
  284.       // Running under 1.3, we have to do this the old fashion way
  285.  
  286.       //
  287.       // Just so we don't have to do any inserts/extra copies, we will work
  288.       // from the end of the buffer and insert as we go
  289.       //
  290.       pos = MAX_FILENAME-1;  // Leave room for a '/' on the end sometimes
  291.       buf[--pos] = 0;
  292.       while(lock != 0)
  293.       {
  294.          BPTR parent;
  295.          int len;
  296.  
  297.          //
  298.          // Examine the lock to get the name for it
  299.          //
  300.          Examine(lock, &fib);
  301.  
  302.          //
  303.          // Find the parent of this directory
  304.          //
  305.          parent = ParentDir(lock);
  306.          UnLock(lock);
  307.          lock = parent;
  308.  
  309.          len = strlen(fib.fib_FileName);
  310.          pos -= 1;
  311.  
  312.          if (len > pos)
  313.          {
  314.             //
  315.             // oops, not enough room, just return the name they gave us
  316.             //
  317.             UnLock(lock);
  318.             free(tail);
  319.             return(strdup(name));
  320.          }
  321.          buf[pos] = lock ? ':' : '/';
  322.          pos -= len;
  323.          memcpy(buf+pos, fib.fib_FileName, len);
  324.       }
  325.    }
  326.  
  327.    //
  328.    // We have the path part in the buffer and the name part in the tail
  329.    // All that is left is to concatenate them together correctly
  330.    //
  331.    {
  332.       int len;
  333.  
  334.       //
  335.       // Successful, the buf holds the path for the directory.  We will need
  336.       // to add a / to the end if it doesn't end in a colon
  337.       //
  338.       len = strlen(buf+pos);
  339.       if ((buf[pos+len-1] != ':') && (buf[pos+len-1] != '/'))
  340.       {
  341.          buf[pos+len++] = '/';
  342.          buf[pos+len] = 0;
  343.       }
  344.       name = malloc(len+strlen(tail)+1);
  345.       if (name != NULL)
  346.       {
  347.          strcpy(name, buf+pos);
  348.          strcpy(name+len, tail);
  349.       }
  350.    }
  351.    return(name);
  352. }
  353.  
  354.  
  355. //***********************************************************************
  356. //* Procedure: SetStem
  357. //* Synopsis:  rc = SetStem(msg, stemstr)
  358. //* Purpose:   Set the appropriate AREXX stem variables based on the
  359. //*            current error message
  360. //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  361. //*
  362. //*  This sets the appropriate AREXX stem variables for the return
  363. //*     FILE:   The name of the file to edit
  364. //*     DIR:    The directory that the file is relative to
  365. //*     LINE:   The line number of the file to go to
  366. //*     ARGS:   The REXX arguments associated with the compile command
  367. //*     COL:    The column number in the file
  368. //*     ERRNO:  The error number
  369. //*     STRING: The error message to be printed out
  370. //*     TEXT:   The complete text of the original line
  371. //*     FPATH:  The full pathname of the file
  372. //*
  373. //***********************************************************************
  374. int SetStem(void *rxmsg, char *stem)
  375. {
  376.    char stembuf[40];
  377.    char *p;
  378.    char *str, *t;
  379.  
  380.    strncpy(stembuf, stem, 32);
  381.    stembuf[32] = 0;
  382.    p = stembuf+strlen(stembuf);
  383.  
  384.    if (curfile == NULL)      return(5);
  385.    if (curfile->cur == NULL) return(5);
  386.  
  387.    strcpy(p, ".DIR");
  388.    SetRexxVar(rxmsg, stembuf, curfile->dir, strlen(curfile->dir));
  389.  
  390.    strcpy(p, ".ARGS");
  391.    SetRexxVar(rxmsg, stembuf, curfile->args, strlen(curfile->args));
  392.  
  393.    str = curfile->cur->line;   // DC1: "fails.c" L:1 C:1 W:68 expected semicolon
  394.  
  395.    strcpy(p, ".TEXT");
  396.    SetRexxVar(rxmsg, stembuf, str, strlen(str));
  397.  
  398.    str = strchr(str, '\"');      // Locate the initial quote for the filename
  399.    if (str == NULL) return(5);   // Skip out if we don't find it.
  400.    str++;                        // Skip over the initial quote
  401.    t = strchr(str, '\"');        // Now look for the terminting quote
  402.    if (t == NULL) return(5);     // and skip out if it is not there
  403.    strcpy(p, ".FILE");
  404.    SetRexxVar(rxmsg, stembuf, str, t-str);
  405.  
  406.    //
  407.    // Get the full file name also.  For this we need to set the current
  408.    // directory and then use the fullpath routine
  409.    //
  410.    chdir(curfile->dir);
  411.    {
  412.       char c;
  413.       char *path;
  414.  
  415.       c = *t;                    // Remember the char that terminated the name
  416.       *t = '\0';                 // and replace it with a null
  417.       path = full_path(str);     // so that we can get the full path of the file
  418.       *t = c;                    // Put the original character back
  419.       strcpy(p, ".FPATH");
  420.       if (path)
  421.       {
  422.          SetRexxVar(rxmsg, stembuf, path, strlen(path));
  423.          free(path);
  424.       }
  425.       else
  426.       {
  427.          //
  428.          // We couldn't get a full path, just punt and use what we started with
  429.          //
  430.          SetRexxVar(rxmsg, stembuf, str, t-str);
  431.       }
  432.    }
  433.  
  434.    str = t + 1;                  // This should either be a NULL or the space
  435.  
  436.    //
  437.    // Parse out the L:<number> to get the line number
  438.    //
  439.    while (*str == ' ') str++;
  440.    t = str;
  441.    if (str[0] == 'L' && str[1] == ':')
  442.    {
  443.       str += 2;                  // Skip over the 'L:'
  444.       t = str;
  445.       while(*t >= '0' && *t <= '9') t++;
  446.    }
  447.    strcpy(p, ".LINE");
  448.    SetRexxVar(rxmsg, stembuf, str, t-str);
  449.    str = t;
  450.  
  451.    //
  452.    // Parse out the C:<number> to get the column number
  453.    //
  454.    while (*str == ' ') str++;
  455.    t = str;
  456.    if (str[0] == 'C' && str[1] == ':')
  457.    {
  458.       str += 2;                  // Skip over the 'C:'
  459.       t = str;
  460.       while(*t >= '0' && *t <= '9') t++;
  461.    }
  462.    strcpy(p, ".COL");
  463.    SetRexxVar(rxmsg, stembuf, str, t-str);
  464.    str = t;
  465.  
  466.    //
  467.    // Save away the error message to be displayed
  468.    //
  469.    while (*str == ' ') str++;
  470.    strcpy(p, ".STRING");
  471.    SetRexxVar(rxmsg, stembuf, str, strlen(str));
  472.  
  473.    //
  474.    // Lastly look for the remaining colon to get the error number
  475.    //
  476.    while(*str && *str != ':') str++;
  477.  
  478.    t = str;
  479.    if (str[0] == ':')
  480.    {
  481.       str++;
  482.       t = str;
  483.       while(*t >= '0' && *t <= '9') t++;
  484.  
  485.    }
  486.    strcpy(p, ".ERRNO");
  487.    SetRexxVar(rxmsg, stembuf, str, t-str);
  488.  
  489.    return(0);
  490. }
  491.  
  492. //***********************************************************************
  493. //* Procedure: do_next
  494. //* Synopsis:  do_next()
  495. //* Purpose:   Advance the current pointers to the next entry
  496. //*            If nothing is active, we wrap around to the beginning again
  497. //***********************************************************************
  498. void do_next(void)
  499. {
  500.  
  501.    if (curfile != NULL &&                    // Do we have any file active?
  502.        curfile->cur->next != &curfile->base) // With at least one more line?
  503.    {
  504.        //
  505.        // Normal case, Everything is fine, just advance to the next line
  506.        //
  507.        curfile->cur = curfile->cur->next;
  508.    }
  509.    else
  510.    {
  511.       //
  512.       // If there is no line active, go to the first one
  513.       // This can happen when they go past the end of the error messages
  514.       // or when they start out for the first time
  515.       //
  516.       if (curfile == NULL)
  517.          curfile = &files;
  518.  
  519.       //
  520.       // Now advance to the next file
  521.       //
  522.       curfile = curfile->next;
  523.  
  524.       if (curfile == &files)   // But make sure we actually have one
  525.       {
  526.          curfile = NULL;       // oops, no files at all, let them know
  527.       }
  528.       else
  529.       {
  530.          //
  531.          // Mark the first line as active.  Note that we can assume
  532.          // there there is always at least one line in a given file
  533.          // because of the way that do_load() works.
  534.          //
  535.          curfile->cur = curfile->base.next;
  536.       }
  537.    }
  538. }
  539.  
  540. //***********************************************************************
  541. //* Procedure: do_prev
  542. //* Synopsis:  do_prev()
  543. //* Purpose:   Advance the current pointers to the previous entry.
  544. //*            If we are at the beginning, don't do anything.
  545. //***********************************************************************
  546. void do_prev(void)
  547. {
  548.    //
  549.    // If there is no line active, go to the first one
  550.    // This can happen when they go past the end of the error messages
  551.    // or when they start out for the first time
  552.    //
  553.    if (curfile)        // Do we have any line active?
  554.    {
  555.       //
  556.       // Make sure we actually have a prev in the current source file
  557.       //
  558.       if (curfile->cur->prev == &curfile->base)
  559.       {
  560.          //
  561.          // No, we need to advance to the next file
  562.          //
  563.          curfile = curfile->prev;
  564.          if (curfile == &files)  // OOps, are we out of files?
  565.          {
  566.             curfile = NULL;
  567.          }
  568.          else
  569.          {
  570.             curfile->cur = curfile->base.prev;
  571.          }
  572.       }
  573.       else
  574.       {
  575.          //
  576.          // Everything is fine, just backup to the prev line
  577.          //
  578.          curfile->cur = curfile->cur->prev;
  579.       }
  580.    }
  581. }
  582.  
  583. //************************************************************************
  584. //* Procedure: do_load
  585. //* Synopsis:  lines = do_load(file, dir, source, args);
  586. //* Purpose:   Load a file into the error parsing.  A string constaining
  587. //*            the lines whcih corrspond to the source is returned.  This
  588. //*            string is not to be freed.
  589. //***********************************************************************
  590. char *do_load(char *efile, char *dir, char *source, char *args)
  591. {
  592.    char *result_append;
  593.    FILE *fh;
  594.    struct FileInfo *fi;
  595.    int result_space;
  596.  
  597.    fh = fopen(efile, "r");
  598.    if (fh == NULL)
  599.       return(NULL);
  600.  
  601.    result_append = ebuf;
  602.    result_space = sizeof(ebuf)-2;
  603.  
  604.    // See if the file is already in our list of things that are active
  605.    for (fi = files.next; fi != &files; fi = fi->next)
  606.    {
  607.       if (!stricmp(fi->source, source) &&
  608.           !stricmp(fi->dir,    dir))
  609.       {
  610.           // We found the existing file.  Just get rid of it
  611.           FreeFile(fi);
  612.           break;
  613.       }
  614.    }
  615.  
  616.    fi = NULL;
  617.    while(fgets(buf, MAX_FILENAME, fh) != NULL)
  618.    {
  619.       int len;
  620.       char *p, *line_text, *file_text;
  621.  
  622.       len = strlen(buf);
  623.       if (len && (buf[len-1] == '\n'))
  624.          buf[len-1] = '\0';
  625.  
  626.       //
  627.       // We need to do some sanity checking here on the line to make sure
  628.       // that it is really an error.  If not, we should just skip the line
  629.       // and go to the next one.  We only have to do a walk through on the
  630.       // line to see that it conforms to some basic sanity.  We will assume
  631.       // That pathological cases don't have to be parsed here because the
  632.       // SetStem routine does more rigerous parsing.
  633.       //
  634.  
  635.       // DC1: "fails.c" L:1 C:1 W:68 expected semicolon
  636.       if ((file_text = strchr(buf, '"'))  && // Find the first quote
  637.           (p = strchr(file_text+1, '"'))  && // And the closing quote
  638.           (line_text = strchr(p,   'L'))  && // Note, we are remembering the L: place
  639.           (line_text[1] == ':')           && // It should be followed by a :
  640.           (p = strchr(line_text+2, ':'))  && // Look for the : in C:
  641.           (p = strchr(p+1, ':')) )           // Look for the : after the error type
  642.       {
  643.          if (fi == NULL)
  644.          {
  645.             // Add the source file to the list of files
  646.             fi = NewFile(source, dir, args);
  647.  
  648.             // Of course if we don't have memory we want to leave gracefully
  649.             if (fi == NULL) return(NULL);
  650.  
  651.             curfile = fi;  // Set it up so we start at the current file
  652.          }
  653.  
  654.          //
  655.          // See if we also need to squirrel away the error line number
  656.          // for the result string.  We need to do this in the case where
  657.          // the source file of this message is the one that we are loading for
  658.          //
  659.          if (!memcmp(file_text+1, source, strlen(source)))
  660.          {
  661.             // Yes, it appears to be what we are looking for.
  662.             line_text += 2;                 // Skip over the L:
  663.             while (result_space &&          // Make sure there is room in the buffer
  664.                    (*line_text >= '0') &&
  665.                    (*line_text <= '9'))     // and copy the numbers which follow it
  666.             {
  667.                *result_append++ = *line_text++;
  668.                result_space--;
  669.             }
  670.             if (result_space)
  671.             {
  672.                *result_append++ = ' ';
  673.                result_space--;
  674.             }
  675.          }
  676.  
  677.          AddLine(fi, buf);
  678.       }
  679.    }
  680.  
  681.    *result_append = 0;    // Null terminate the result
  682.    if (curfile != NULL)
  683.    {
  684.       //
  685.       // Mark the first line as active.  Note that we can assume
  686.       // there there is always at least one line in a given file
  687.       // because of the way that do_load() works.
  688.       //
  689.       curfile->cur = curfile->base.next;
  690.    }
  691.    fclose(fh);
  692.    return(ebuf);
  693. }
  694.  
  695.  
  696. //***********************************************************************
  697. //* Procedure: do_ttx
  698. //* Synopsis:  do_ttx(file, macro, project)
  699. //* Purpose:   Invoke turbotext on a given file
  700. //*
  701. //***********************************************************************
  702. char *do_ttx(char *file, char *project, char *macro)
  703. {
  704.    char *document_list, *portname;
  705.  
  706.    portname = NULL;
  707.  
  708.    //
  709.    // Make sure that TurboText is actually running
  710.    //
  711.    {
  712. #define FIND_ITERATIONS 5
  713.       short Port_Find;
  714.  
  715.       for(Port_Find = FIND_ITERATIONS; Port_Find > 0; Port_Find--)
  716.       {
  717.           if (FindPort("TURBOTEXT") != NULL) break;
  718.           if (Port_Find == FIND_ITERATIONS)  // First time through
  719.              Execute("Turbotext:TTX BACKGROUND NOWINDOW", 0L, 0L);
  720.       Delay(50);
  721.       }
  722.       //
  723.       // Make sure that we were acutally able to start turbotext before
  724.       // going forward
  725.       //
  726.       if (Port_Find == 0)
  727.          return(NULL);
  728.    }
  729.  
  730.    //
  731.    // Send the command off to rexx to be processed
  732.    // We will wait here until it is complete
  733.    document_list = dottx(NULL, "GETDOCUMENTS");
  734.    if (document_list != NULL)
  735.    {
  736.       char *fname;
  737.  
  738.       //
  739.       // Now we need to go through and figure out all the files that are there
  740.       // Turbotext will return us a string in the form
  741.       //   "file1" TURBOTEXT1 "file2" TURBOTEXT2
  742.       // Unfortunately there are some pathological cases (such as having filenames
  743.       // with quotes in them and files having the string TURBOTEXT in them which
  744.       // make it hard to parse 100% accurately.
  745.       //
  746.       for(fname = document_list; *fname;)
  747.       {
  748.          char *file_path, *t;
  749.          int length;
  750.  
  751.          //
  752.          // We expect the string to be well formed.  Fall out on any
  753.          // pathological cases.
  754.          //
  755.          if (*fname != '"')
  756.          {
  757.             say("TURBOTEXT Sync Error");
  758.             free(document_list);
  759.             return("");
  760.          }
  761.          fname++;
  762.          t = fname;
  763.          length = strlen(t);
  764.          while ((length > 11) && memcmp(t, "\" TURBOTEXT", 11))
  765.          {
  766.             char *quote_pos;
  767.  
  768.             //
  769.             // We need to find the Quote which is immediately before
  770.             // the TURBOTEXT string
  771.             //
  772.             quote_pos = strchr(t+1, '"');
  773.             if (quote_pos == NULL)
  774.                break;
  775.             length -= (quote_pos-t);
  776.             t = quote_pos;
  777.          }
  778.  
  779.          //
  780.          // At this point, t should be pointing at the " in the name
  781.          //
  782.          *t = 0;
  783.  
  784.          //
  785.          // Now we want to get the portname for the file
  786.          //
  787.          t += 2;                 // Skip the '" '
  788.          portname = t;
  789.          while(*t && (*t != ' ')) t++;
  790.          if (*t) *t++ = 0;       // Null terminate it if necessary
  791.  
  792.          file_path = dottx(portname, "GetFilePath");
  793.  
  794.          //
  795.          // If this is the file that we are interested in, drop out of the
  796.          // loop and go to work on it in the file
  797.          //
  798.          if ((file_path != NULL) && !stricmp(file, file_path))
  799.          {
  800.             free(file_path);
  801.             portname = strdup(portname);
  802.             break;
  803.          }
  804.  
  805.          if (file_path != NULL) free(file_path);
  806.  
  807.          //
  808.          // Advance to let the next stuff work
  809.          //
  810.          fname = t;
  811.          while(*fname == ' ') fname++;
  812.          portname = NULL;
  813.       }
  814.       free(document_list);
  815.    }
  816.  
  817.  
  818.    //
  819.    // If we got a match, the port will tell us who to talk to
  820.    //
  821.    if (portname == NULL)
  822.    {
  823.       //
  824.       // No match here, we need to open up the file
  825.       //
  826.       char *open_cmd;
  827.  
  828.       open_cmd = malloc(strlen(file) + 15);  // strlen("OPENDOC NAME ")
  829.       if (open_cmd)
  830.       {
  831.          strcpy(open_cmd, "OPENDOC NAME ");
  832.          strcat(open_cmd, file);
  833.          portname = dottx(NULL, open_cmd);
  834.          free(open_cmd);
  835.       }
  836.    }
  837.    else
  838.    {
  839.       //
  840.       // The file is already open, we just need to bring it forward
  841.       //
  842.       char *pr;
  843.       pr = dottx(portname, "WINDOW2FRONT");
  844.       if (pr != NULL) free(pr);
  845.       pr = dottx(portname, "SCREEN2FRONT");
  846.       if (pr != NULL) free(pr);
  847.    }
  848.  
  849.    if (portname != NULL)
  850.    {
  851.       char *p;
  852.  
  853.       p = dottx(portname, "ACTIVATEWINDOW");
  854.       if (p != NULL) free(p);
  855.  
  856.       //
  857.       // We now have the document open, portname is the port to talk to
  858.       //
  859.       if (project != NULL)
  860.       {
  861.          char *call_cmd;
  862.          //
  863.          // ExecARexxString call setclip(TTX_TURBOTEXT12,myport.1)
  864.          // 000000000111111111122222222223333           3        3
  865.          // 123456789012345678901234567890123           4        5
  866.          //
  867.          call_cmd = malloc(strlen(project)+strlen(portname)+36);
  868.          if (call_cmd)
  869.          {
  870.             strcpy(call_cmd, "ExecARexxString call setclip(TTX_");
  871.             strcat(call_cmd, portname);
  872.             strcat(call_cmd, ",");
  873.             strcat(call_cmd, project);
  874.             strcat(call_cmd, ")");
  875.             p = dottx(portname, call_cmd);
  876.             if (p != NULL) free(p);
  877.             free(call_cmd);
  878.          }
  879.       }
  880.  
  881.       //
  882.       // Next we want to issue the macro command to make it run
  883.       //
  884.       if (macro != NULL)
  885.       {
  886.          char *p;
  887.          p = dottx(portname, macro);
  888.          if (p != NULL) free(p);
  889.       }
  890.    }
  891.  
  892.    //
  893.    // Since this will be returned to rexx, we need to ensure that this is
  894.    // somewhat permanent storage.  Unfortunately, we don't have a way to
  895.    // free the memory easily (we lose our context once the message has been
  896.    // sent back to arexx)
  897.    //
  898.    if (portname)
  899.    {
  900.       strcpy(ebuf, portname);
  901.       free(portname);
  902.       portname = ebuf;
  903.    }
  904.  
  905.    return(portname);
  906. }
  907.  
  908. //***********************************************************************
  909. //* Procedure: DoRexxCommand
  910. //* Synopsis:  DoRexxCommand(msg, port, arg, &result)
  911. //* Purpose:   Handle a incomming AREXX command
  912. //*
  913. //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  914. //*
  915. //*  We handle the following Rexx Commands:
  916. //*     QUIT
  917. //*     NEXT    <stem>
  918. //*     FIRST   <stem>
  919. //*     PREV    <stem>
  920. //*     CURRENT <stem>
  921. //*     CLEAR
  922. //*     TTXSAME "<file>" "<project>" "<macro>"
  923. //*     LOAD    "<error>" "<dir>" "<sourcefile>" <args>
  924. //*
  925. //*   Where <stem> Is any valid rexx variable name
  926. //*
  927. //***********************************************************************
  928.  
  929.  
  930. long
  931. DoRexxCommand(msg, port, arg0, pres)
  932. void *msg;              //  RexxMsg structure if we need it
  933. struct MsgPort *port;   //  MsgPort structure if we need it
  934. char *arg0;             //  arg0
  935. char **pres;            //  where to put our result if rc==0
  936. {
  937.     char cmd[9];
  938.     char *p;
  939.  
  940.     strncpy(cmd, arg0, 8);
  941.     cmd[8] = 0;         // Ensure that the string is properly terminated
  942.     p = strchr(cmd, ' ');
  943.     if (p) *p = 0;
  944.  
  945.     p = arg0+strlen(cmd);
  946.     while (*p == ' ') p++;
  947.  
  948.     //*******************************************************
  949.     //*  QUIT - Terminate the error parser                  *
  950.     //*******************************************************
  951.     if (!stricmp(cmd, "QUIT"))
  952.     {
  953.         run_arexx_server = 0;
  954.         return(0);
  955.     }
  956.  
  957.     //*******************************************************
  958.     //*  CLEAR - Clear out all errors stored                *
  959.     //*******************************************************
  960.     if (!stricmp(cmd, "CLEAR"))
  961.     {
  962.        while(files.next != &files)
  963.           FreeFile(files.next);
  964.        return(0);
  965.     }
  966.  
  967.     //*******************************************************
  968.     //*  TTXSAME - Invoke TTX on a file                     *
  969.     //*******************************************************
  970.     if (!stricmp(cmd, "TTXSAME"))
  971.     {
  972.        int ac;
  973.        char *hold;
  974.        char *av[3];
  975.        int rc = 0;
  976.  
  977.        av[0] = av[1] = av[2] = 0;
  978.        hold = strdup(p);
  979.  
  980.        if (hold == NULL)
  981.        {
  982.           return(10);    // No memory
  983.        }
  984.  
  985.        ac = _parseargs1(hold, strlen(hold));
  986.        if (ac)
  987.        {
  988.           if (ac > 3)
  989.              ac = 3;
  990.           _parseargs2(hold, av, ac);
  991.           av[0] = full_path(av[0]);
  992.           *pres = do_ttx(av[0], av[1], av[2]);
  993.           free(av[0]);
  994.           if (*pres == NULL)
  995.              rc = 5;
  996.        }
  997.        free(hold);
  998.        return(rc);
  999.     }
  1000.  
  1001.     //*******************************************************
  1002.     //*  LOAD - Load errors into the parser                 *
  1003.     //*******************************************************
  1004.     if (!stricmp(cmd, "LOAD"))
  1005.     {
  1006.        int ac;
  1007.        char *hold;
  1008. #define LOAD_PARMS 4
  1009.        char *av[LOAD_PARMS];
  1010.        int rc = 0;
  1011.  
  1012.        av[0] = av[1] = av[2] = av[3] = 0;
  1013.        hold = strdup(p);
  1014.  
  1015.        if (hold == NULL)
  1016.        {
  1017.           return(10);    // No memory
  1018.        }
  1019.  
  1020.        ac = _parseargs1(hold, strlen(hold));
  1021.        if (ac)
  1022.        {
  1023.           if (ac > LOAD_PARMS)
  1024.              ac = LOAD_PARMS;
  1025.           _parseargs2(hold, av, ac);
  1026.           *pres = do_load(av[0], av[1], av[2], av[3]);
  1027.           if (*pres == NULL)
  1028.              rc = 5;
  1029.        }
  1030.        free(hold);
  1031.        return(rc);
  1032.     }
  1033.  
  1034.     //*******************************************************
  1035.     //*  NEXT - Advance to the next error                   *
  1036.     //*******************************************************
  1037.     if (!stricmp(cmd, "NEXT"))
  1038.     {
  1039.        do_next();
  1040.     }
  1041.     //*******************************************************
  1042.     //*  FIRST - Go to the first error                      *
  1043.     //*******************************************************
  1044.     else if (!stricmp(cmd, "FIRST"))
  1045.     {
  1046.        curfile = NULL;
  1047.        do_next();
  1048.     }
  1049.     //*******************************************************
  1050.     //*  PREV - Move to the previous error                  *
  1051.     //*******************************************************
  1052.     else if (!stricmp(cmd, "PREV"))
  1053.     {
  1054.        do_prev();
  1055.     }
  1056.     //*******************************************************
  1057.     //*  CURRENT - Return information on the current error  *
  1058.     //*******************************************************
  1059.     else if (stricmp(cmd, "CURRENT"))
  1060.     {
  1061.         // Unrecognized command, let them know about it
  1062.         *pres = "Command Unknown";
  1063.         return(5);
  1064.     }
  1065.     return(SetStem(msg, p));
  1066. }
  1067.  
  1068.  
  1069. //***********************************************************************
  1070. //* Procedure: main
  1071. //* Synopsis:  rc = main(argc, argv);
  1072. //* Purpose:   Main entry point
  1073. //***********************************************************************
  1074. int main(int argc, char **argv)
  1075. {
  1076.    struct SAVER {
  1077.       struct SAVER *next;
  1078.       char         *fname;
  1079.    };
  1080.  
  1081.    int i;
  1082.    char *macro;
  1083.    char *project;
  1084.    struct SAVER base, *nsaver;
  1085.  
  1086.    run_arexx_server  = 0;
  1087.    files.prev = &files;
  1088.    files.next = &files;
  1089.    curfile = NULL;
  1090.  
  1091.    macro   = NULL;
  1092.    project = NULL;
  1093.    nsaver = &base;
  1094.    base.next = NULL;
  1095.  
  1096.    //
  1097.    // DICE automatically opens rexxsyslib.library for us as long
  1098.    // as we reference the base variable (via extern) and not
  1099.    // declare it.  lib/rexx.h does this for us.
  1100.    //
  1101.    // However, unlike other autoinits, if DICE is unable to open
  1102.    // the library it does not abort the program, hence the following.
  1103.    //
  1104.    if (RexxSysBase == NULL)
  1105.    {
  1106.       say("Unable to open rexxsyslib.library !");
  1107.       exit(20);
  1108.    }
  1109.  
  1110.    if ((argc == 2) && (!strcmp(argv[1], "?"))) usage();
  1111.  
  1112.    // Template for command:
  1113.    //   FILE/M,MACRO/K,PROJECT/K,REXXSTARTUP/S" VERSTAG);
  1114.    for (i = 1; i < argc; i++)
  1115.    {
  1116.       if (!stricmp(argv[i], "MACRO"))
  1117.       {
  1118.          if (++i >= argc) usage();
  1119.          if (macro != NULL) usage();
  1120.          macro = malloc(strlen(argv[i]) + 16); // ExecArexxMacro
  1121.          if (macro)
  1122.          {
  1123.             strcpy(macro, "ExecArexxMacro ");
  1124.             strcat(macro, argv[i]);
  1125.          }
  1126.       }
  1127.       else if (!stricmp(argv[i], "PROJECT"))
  1128.       {
  1129.          if (++i >= argc) usage();
  1130.          project = argv[i];
  1131.       }
  1132.       else if (!stricmp(argv[i], "REXXSTARTUP"))
  1133.       {
  1134.          run_arexx_server = 1;
  1135.       }
  1136.       else
  1137.       {
  1138.          if (!stricmp(argv[i], "FILE"))
  1139.          {
  1140.             if (++i >= argc) usage();
  1141.          }
  1142.          nsaver->next = malloc(sizeof(struct SAVER));
  1143.          if (nsaver->next)
  1144.          {
  1145.             nsaver = nsaver->next;
  1146.             nsaver->fname = full_path(argv[i]);
  1147.             nsaver->next  = NULL;
  1148.          }
  1149.       }
  1150.    }
  1151.  
  1152.    if (run_arexx_server)
  1153.    {
  1154.       short r = CreateGlobalDiceRexxPort(NULL, PORTNAME);
  1155.       if (r < 0)
  1156.       {
  1157.          say("AREXX Port " PORTNAME " exists.  DError is already running\n");
  1158.          exit(20);
  1159.       }
  1160.    }
  1161.    else
  1162.    {
  1163.       CreateDiceRexxPort(NULL, NULL);
  1164.    }
  1165.  
  1166.    // We have parsed our parameters, now we need to load each of the files
  1167.    // specified into the editor.  In the process we will have to check for
  1168.    // Any files in the editor to ensure that they are not already loaded
  1169.    while((nsaver = base.next) != NULL)
  1170.    {
  1171.       char *portname;
  1172.  
  1173.       portname = do_ttx(nsaver->fname, project, macro);
  1174.       if (portname == NULL)
  1175.          say("Unable to edit file");
  1176.  
  1177.       // Take the entry off the list because we have already processed it
  1178.       base.next = nsaver->next;
  1179.       free(nsaver->fname);
  1180.       free(nsaver);
  1181.    }
  1182.  
  1183.    if (run_arexx_server)
  1184.    {
  1185.       //
  1186.       // Our main loop executes received commands
  1187.       //
  1188.       while (run_arexx_server)
  1189.       {
  1190.          long mask = Wait(SIGBREAKF_CTRL_C | (1 << RexxSigBit));
  1191.  
  1192.          if (mask & SIGBREAKF_CTRL_C)
  1193.             break;
  1194.  
  1195.          if (mask & (1 << RexxSigBit))
  1196.              ProcessRexxCommands(NULL);
  1197.       }
  1198.       say("*** DError terminating\n");
  1199.    }
  1200.    return(0);
  1201. }
  1202.